package start.spring.basic.dynamic;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;

import lombok.extern.slf4j.Slf4j;
import start.spring.basic.constant.CommonConstant;
import start.spring.basic.util.common.EncryptUtil;
import start.spring.basic.util.common.StringUtil;

/**
 * ****************************************************************************
 * 动态数据库初始化类
 * 根据默认数据库中的配置，初始化所有需要使用的数据源
 *
 * @author(作者)：xuyongyun	
 * @author(复审人)：	Administrator 2018年8月22日
 * @version(版本)1.0
 * @since 1.0
 ******************************************************************************
 */
@Slf4j
@RefreshScope
@Repository
public class DynamicDSRegister implements EnvironmentAware {
	
	@Autowired
	DynamicRoutingDataSource drd;
	
	private final static String  YES = "yes";
	
	private final static String  AES = "aes";
	
	/**
	 * 缓存JdbcTemplate操作客户端
	 */
    private static Map<String, JdbcTemplate> jdbcTemplate = new HashMap<String, JdbcTemplate>();
	
    @Override
	public void setEnvironment(Environment environment) {
    	log.info("开始初始化数据库配置！");
    	log.info("nacos地址:" + environment.getProperty("spring.cloud.nacos.discovery.server-addr"));
    	log.info("数据库地址:" + environment.getProperty("spring.datasource.dynamic.datasource.master.url"));
    	//读取默认数据库配置
		Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(environment);
		Binder binder = new Binder(sources);
		BindResult<Properties> dbBindResult = binder.bind("spring.datasource.dynamic.druid", Properties.class);
		Properties properties = dbBindResult.get();
		
		initDefaultDS();
		initSysConfigDS();
        initCustomDS(properties);
	}

	/**
     * 初始化主数据源
     *	
     * @since           1.0
     */
	private void initDefaultDS() {
		JdbcTemplate defaultTemplate = new JdbcTemplate(drd.getDataSource(DsConstant.DEFAULT));
        jdbcTemplate.put(DsConstant.DEFAULT, defaultTemplate);
        log.info("数据源:" + DsConstant.DEFAULT + "初始化完毕！");
	}
	
	/**
     * 初始化系统配置数据源
     *	
     * @since           1.0
     */
	private void initSysConfigDS() {
		JdbcTemplate defaultTemplate = new JdbcTemplate(drd.getDataSource(DsConstant.SYSCONFIG));
        jdbcTemplate.put(DsConstant.SYSCONFIG, defaultTemplate);
        log.info("数据源:" + DsConstant.DEFAULT + "初始化完毕！");
	}

	/**
	 * 初始化其它数据源
	 *	
	 * @param environment
	 * @since           1.0
	 */
	private void initCustomDS(Properties properties) {
		log.info("DynamicDSRegister.initCustomDS()");
		//读取配置文件获取更多数据库配置
        String dsPrefixs = properties.getProperty("customnames");
        
        //需要加载其它数据源
        if(!StringUtil.isEmpty(dsPrefixs)) {
        	String sql = "select * from GG_PZ_DB";
        	List<Map<String, Object>> dbConfigList = getTemple(DsConstant.SYSCONFIG).queryForList(sql);
        	
        	if(dbConfigList!=null&&dbConfigList.size()>0) {
        		
        		for(int i=0;i<dbConfigList.size();i++) {
        			Map<String, Object> dsMap = dbConfigList.get(i);
        			String dbKey = dsMap.get("DBID").toString().trim();
        			if(dsPrefixs.indexOf(dbKey)>=0) {
        				dsMap.put("aes", "no");
                        
                        JdbcTemplate jTemplate = buildTemplate(dsMap,properties);
                        jdbcTemplate.put(dbKey, jTemplate);
                        
                        drd.addDataSource(dbKey, jTemplate.getDataSource());
                        log.info("数据库:" + dbKey + "初始化完毕！");
        			}
        		}
        		
        	}else {
        		log.info("未从GG_PZ_DB查询到需要初始化的数据库连接信息!");
        	}
        }
	}
	
	private JdbcTemplate buildTemplate(Map<String, Object> dsMap, Properties properties) {
		try {
	        JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSource(dsMap,properties));
	        return jdbcTemplate;
		}catch(Exception e) {
			log.error("初始化数据源出错！",e);
		}
		return null;
	}
	
	private DruidDataSource getDataSource(Map<String, Object> dsMap,Properties properties){
		
		if(dsMap!=null) {
			
			String url = dsMap.get("URL").toString();
			String username = dsMap.get("USERNAME").toString();
			String password = dsMap.get("PWD").toString();
			
			DruidDataSource dataSource = new DruidDataSource();
		    dataSource.setUrl(url);
		    dataSource.setUsername(username);
		    
		    if (DynamicDSRegister.YES.equals((String.valueOf((dsMap.get(DynamicDSRegister.AES)))))) {
			    try {
			    	//对数据库中的密码进行aes解密，属性文件中的密码不做解密操作
					password = EncryptUtil.decryptAes(password, 
							CommonConstant.DB_KEY_AES);
				} catch (Exception e) {
					log.error(url + "数据库密码解密出错！",e);
				}
		    }
		    dataSource.setPassword(password);
		    
		    dataSource.setDriverClassName(dsMap.get("DRIVER_CLASS_NAME").toString());
		    dataSource.setValidationQuery(dsMap.get("VALIDATION_QUERY").toString());
		    dataSource.setInitialSize(Integer.parseInt(dsMap.get("INIT_SIZE").toString()));
		    dataSource.setMaxActive(Integer.parseInt(dsMap.get("MAX_ACTIVE").toString()));
		    dataSource.setMinIdle(Integer.parseInt(dsMap.get("MIN_IDLE").toString()));
		    
		    dataSource.setTimeBetweenEvictionRunsMillis(
		    		Long.parseLong(properties.get("time-between-eviction-runs-millis").toString()));
		    dataSource.setMinEvictableIdleTimeMillis(
		    		Long.parseLong(properties.get("min-evictable-idle-time-millis").toString()));
		    dataSource.setRemoveAbandoned(Boolean.parseBoolean(
		    		properties.get("remove-abandoned").toString()));
		    if(dataSource.isRemoveAbandoned()) {
		    	dataSource.setLogAbandoned(true);
		    	String removeAbandonedTimeout = "1800000";
			    if(properties.containsKey("remove-abandoned-timeout-millis")) {
			    	removeAbandonedTimeout = properties.get("remove-abandoned-timeout-millis").toString();
			    	if(StringUtil.isEmpty(removeAbandonedTimeout)) {
			    		removeAbandonedTimeout = "1800000";
			    	}
			    }
			    dataSource.setRemoveAbandonedTimeout(Integer.parseInt(removeAbandonedTimeout));
		    }
		    dataSource.setTestWhileIdle(Boolean.parseBoolean(
		    		properties.get("test-while-idle").toString()));
		    dataSource.setTestOnBorrow(Boolean.parseBoolean(
		    		properties.get("test-on-borrow").toString()));
		    dataSource.setMaxOpenPreparedStatements(
		    		Integer.parseInt(properties.get("max-pool-prepared-statement-per-connection-size").toString()));
		    dataSource.setFailFast(Boolean.parseBoolean(
		    		properties.get("fail-fast").toString()));
		    dataSource.setPoolPreparedStatements(Boolean.parseBoolean(
		    		properties.get("pool-prepared-statements").toString()));
		    try {
				dataSource.setFilters("stat");
			} catch (SQLException e) {
				e.printStackTrace();
			}
		    return dataSource;
		    
		}else {
			return null;
		}
	}
	
	/**
	 * 外部获取已初始化的JdbcTemplate的方法
	 *	
	 * @param dsKey JdbcTemplate对应的key,不传返回默认数据库
	 * @return
	 * @since           1.0
	 */
	public JdbcTemplate getTemple(String dsKey) {
		if(!StringUtil.isEmpty(dsKey)&&jdbcTemplate.containsKey(dsKey)) {
			return jdbcTemplate.get(dsKey);
		}else {
			return jdbcTemplate.get(DsConstant.DEFAULT);
		}
	}
	
}
